#ifndef B_PHYSICS_ACTOR_H_
#define B_PHYSICS_ACTOR_H_

class DirectXMesh;
class btRigidBody;
class btCollisionShape;
class BPhysicsMotionState;
class BehaviourI;
class btTriangleIndexVertexArray;
class btTriangleMesh;
class Grid;
class btRaycastVehicle;
struct btVehicleRaycaster;

struct ID3DXEffect;
#include <stack>
#include <d3dx9.h>
#include <LinearMath\btVector3.h>
#include <LuaPlus.h>
#include "Lua_State_Manager.h"
#include "AI_Perceiver.h"
#include "Ai_Message.h"
#include "BehaviourInfo.h"
#include "Camera.h"
#include "Audio.h"

/* This class ties together Bullet physics, my AI classes and
   my DirectX Mesh class. This would usually be a Base class,
   and each derived type would manage one type of physics with one
   type of AI & rendering. In this case, this class manages everything.
*/
class BPhysicsActor : public MessageSystem::Message_Handler
{
/************************************************************************/
/* MESSAGING                                                            */
/************************************************************************/
public:
    virtual void vSendMessage(MessageSystem::Ai_Message msg){m_outBox.push_back(msg);}
    virtual void vReceiveMessage(MessageSystem::Ai_Message msg){m_inBox.push_back(msg);}
    virtual MessageSystem::Messages& vGetOutBox(){return m_outBox;}
    virtual void vProcessInbox();
private:
    MessageSystem::Messages m_outBox;
    MessageSystem::Messages m_inBox;

/************************************************************************/
/* LUA                                                                  */
/************************************************************************/
public:
    LuaPlus::LuaObject GetMetaTable();
    void LSetPosition(float x, float y, float z);
    void LChangeState(LuaPlus::LuaObject newState);
    void LApplyForce(float x, float y, float z);
    void LSetVelocity(float x, float y, float z);
    void LAdjustPosition(float x, float y, float z);
    void InitializeStateManager(const char* luaObj);
    void LUpdateBehaviour();
    void LSetBehaviour(int name);
    void LPostMessage(int which, float time);
    void LSetUpBehaviourInfo(float maxVelocity, int targetNode, float radius);
    void LSetUpPerceptionOrthographic(int resX, int resY,int width, int height, float n, float f);
    void LSetUpPerceptionPerspective(int resX, int resY,float fOv, float aspect, float n, float f);
    int LGetNearestPathNode();
    static LuaPlus::LuaObject* StaticInitializeLuaMetaTable();
    static LuaPlus::LuaObject* StaticGetLuaMetaTable(){return Static_MetaTable;}
private:
    static LuaPlus::LuaObject* Static_MetaTable;

/************************************************************************/
/* TYPING                                                                */
/************************************************************************/
public:
    enum ACTOR_TYPE
    {
        Actor_Dynamic,
        Actor_Boring,
    };
    ACTOR_TYPE GetType(){return m_type;}
private:
    ACTOR_TYPE m_type;

/***********************************************************************  */
/* INITIALIZATION                                                         */
/***********************************************************************  */
public:
    BPhysicsActor(float mass, const btVector3& scale, Grid* g = NULL);
    bool CreateFromMesh(const char* name,bool staticPhysics);
    bool CreateFromMesh(DirectXMesh* mesh,bool staticPhysics);
    void CreateAsPlane(const btVector3& normal, float offset, const btVector3& drawingScale );
    void CreateAsCube();
    void CreateAsSphereObstacle(float radius);
    void CreateAsWallObstacle(const btVector3& from, const btVector3& to, float thickNess);
    bool CreateAsVehicle( const char* name);
    void SetMesh(DirectXMesh* mesh){m_mesh = mesh;}
/************************************************************************/
/* SHUT DOWN                                                            */
/************************************************************************/
public:
    void CleanUp();
    
/************************************************************************/
/* MAIN UPDATE FUNCTIONS                                                */
/************************************************************************/
public:
    void Update(float dt);
    void Render(ID3DXEffect* effect, Camera* cam);
private:
    void Think(float dt);
    void ProcessPerceptionBuffer();

/************************************************************************/
/* HELPER FUNCTIONS                                                     */
/************************************************************************/
public:
    /***************/
    /* PHYSICS     */
    /***************/
    int HasBeenAddedToMap() const {return m_addedToMap;}
    void AddedToMap(){m_addedToMap++;}
    bool IsAwake() const {return m_isAwake;}
    void SetAwake(bool awake){m_isAwake = awake;}
    btRigidBody* GetBodyPointer();
    void GenerateBody();
    void CreateConvexHull(D3DXMATRIXA16& initialTransform);
    void CreateBVH(D3DXMATRIXA16& initialTransform);
    btCollisionShape* GetShapePointer();
    const BPhysicsMotionState& GetMotionState() const;
    void SetActive(bool active);
    void SetPosition(const btVector3& pos);
    void ApplyCentralForce(const btVector3& force);
    void ApplyForceAtPoint(const btVector3& force,const btVector3& point);
    void SetFriction( float fric );
    void SteerLeft(float val);
    void SteerRight(float val);
    btRaycastVehicle* GetVehiclePointer(){return m_vehicle;}
public:
    /***************/
    /* AI          */
    /***************/
    btVector3 GetPosition() const;
    const btVector3& GetLinearVelocity() const;
    bool Thinking() const { return m_thinking; }
    void SetThinking(bool val) { m_thinking = val; }
    BPhysicsActor* Target() const { return m_target; }
    void SetTarget(BPhysicsActor* val) { m_target = val;m_info.SetTargetActor(val); }
    const btVector3& GetHeading() const { return m_heading; }
    void SetHeading(const btVector3& val) { m_heading = val; }
    const btVector3& GetRightVector() const { return m_rightVector; }
    float GetMaxVelocity() const { return m_maxVelocity; }
    void SetMaxVelocity(float val) { m_maxVelocity = val; }
    float GetAiCollisionRadius() const { return m_AiCollisionRadius; }
    void SetAiCollisionRadius(float val) { m_AiCollisionRadius = val; }
    bool IsStaticObstacle() const {return m_isStaticObstacle;}
    DirectXMesh* GetMesh();
    void SteerCentre();
    void ApplyBrakes( float param1 );
    void UpdateGrid() const;
    BPhysicsMotionState* GetMotionStatePointer();
    bool HasTarget();
    BPhysicsActor* GetTarget();
    Grid* GetGrid();
    void CreateAi( BPhysicsActor* target, float aiRadius, 
        float maxVel, bool startThinking, const char* luaObj);
    void CreatePerceptionOccluder(Perception::Ai_Perception_Key key, DirectXMesh* meshName);
    //perception
    Perception::Ai_Perceiver* GetPerceptionInterface()
    {
        return m_perceiver;
    }
    void CreateOrthoProjection(int width, int height, float n, float f);
    void CreatePerspProjection(float fOv, float aspect, float n, float f);
    void SetUpPerception(int x, int y);
    int GetProjectionType(){return m_projectionType;}
    void CreateEngineAudio();
    /***************/
/* MEMBER DATA */
/***************/
/************************************************************************/
/* RIGID BODY DATA                                                      */
/************************************************************************/
private:
    btRigidBody* m_body;
    btCompoundShape* m_shape;
    BPhysicsMotionState* m_motionState;
    float m_mass;
    float m_AiCollisionRadius;
    btVector3 m_scale;
    btTransform m_transform;
    int m_addedToMap;

/************************************************************************/
/* MESH DATA                                                            */
/************************************************************************/
    int* m_meshIndexs;
    btScalar* m_meshVertexs;
    //optional data used in creation, may need deleting
    btTriangleIndexVertexArray* m_array;
    btTriangleMesh* m_triangleMesh;    

/************************************************************************/
/* RENDERING DATA                                                       */
/************************************************************************/
    DirectXMesh* m_mesh;

/************************************************************************/
/* AI DATA                                                              */
/************************************************************************/
    Lua_State_Manager<BPhysicsActor> m_manager;
    BehaviourI* m_behaviour;
    BehaviourInfo m_info;
    bool m_thinking;
    BPhysicsActor* m_target;
    btVector3 m_heading;
    btVector3 m_rightVector;
    float m_maxVelocity;
    bool m_isStaticObstacle;
    Grid* m_grid;
    bool m_isAwake;
    //perception stuff
    Perception::Ai_Perception_Obj* m_aip_Obj;
    D3DXMATRIXA16 m_d3dTransform;
    Perception::Ai_Perceiver* m_perceiver;
    float m_averageColor;
    int m_resX;
    int m_resY;
    int m_projectionType; //0 for perspective, 1 for ortho
/************************************************************************/
/* VEHICLE DATA                                                         */
/************************************************************************/
    btRaycastVehicle* m_vehicle;
    btVehicleRaycaster* m_rayCaster;
    DirectXMesh* m_tyreMesh;

    float m_steeringValue;
    float m_breakingForce;
    float m_maxBrakes;
    float m_steerResetSpeed;

    Audio_Engine::Position_Node* m_engineNode;
    Audio_Engine::Wav_Player* m_wavPlayerNode;

/************************************************************************/
/* NETWORKED INPUT                                                      */
/************************************************************************/
public:
    enum CONTROL
    {
        FORWARD,
        BACKWARD,
        LEFT,
        RIGHT,
        BOOST,
        BRAKE,
        NUM_CONTROLLS
    };
    void ButtonHeld(CONTROL which, bool pressed)
    {
        m_controlls[which] = pressed;
    }
    void HandleInputs();
private:
    bool m_controlls[NUM_CONTROLLS];
};

#endif